今天要完成圖書管理系統中的修改書籍 edit_book.php 與刪除書籍 delete_book.php,並在首頁 index.php 新增操作欄位來串接這些功能。。
編輯功能分為兩階段:
edit_book.php?id=1),去資料庫撈出該書籍目前的資料並填入表單。給 Claude Code 的 Prompt:
請幫我在 ~/library-system/edit_book.php 建立編輯書籍頁面:
1. 引進 config/database.php 並取得 PDO 連線。
2. 接收 GET 參數 id,若 id 不存在或資料庫找不到該書籍,自動重新導向至 index.php。
3. GET 請求時,將該書籍目前的資料帶入 HTML 表單的 input 欄位中。
4. POST 請求時,接收表單欄位:title(書名)、author(作者)、isbn(ISBN)、category(類別)、quantity(數量)。
5. 進行基本資料驗證(必填檢查、數量必須為 >= 0 的整數)。
6. 使用 Prepared Statement 執行 UPDATE 語法更新 books 資料表,注意可借數量 (available) 需依據總數量 (total) 的增減調整,或保持合理性。
7. 成功更新後重新導向至 index.php。
8. 捕捉 ISBN 重複例外(SQLSTATE 23000)並給予友善提示。
9. 前端 HTML5 表單樣式需與 add_book.php 保持一致。
edit_book.php程式碼:
<?php
require __DIR__ . '/config/database.php';
$pdo = Database::getConnection();
// 從網址參數取得要編輯的書籍 id,格式不合法就導回列表頁
$id = $_GET['id'] ?? null;
if ($id === null || !ctype_digit((string) $id) || (int) $id < 1) {
header('Location: index.php');
exit;
}
$id = (int) $id;
// 查出這本書目前的資料,作為表單預設值
$stmt = $pdo->prepare("SELECT id, title, author, isbn, category, total, available FROM books WHERE id = :id");
$stmt->execute([':id' => $id]);
$book = $stmt->fetch();
// 查無此書(id 不存在)也導回列表頁
if ($book === false) {
header('Location: index.php');
exit;
}
$errors = [];
$title = $book['title'];
$author = $book['author'];
$isbn = $book['isbn'];
$category = $book['category'];
$quantity = $book['total'];
// 只有表單送出(POST)時才驗證與更新資料庫,GET 只是顯示帶預設值的表單
if ($_SERVER['REQUEST_METHOD'] === 'POST') {
$title = trim($_POST['title'] ?? '');
$author = trim($_POST['author'] ?? '');
$isbn = trim($_POST['isbn'] ?? '');
$category = trim($_POST['category'] ?? '');
$quantity = trim($_POST['quantity'] ?? '');
if ($title === '') {
$errors[] = '請輸入書名';
}
if ($author === '') {
$errors[] = '請輸入作者';
}
if ($isbn === '') {
$errors[] = '請輸入 ISBN';
}
if ($quantity === '' || !ctype_digit($quantity) || (int) $quantity < 0) {
$errors[] = '數量請輸入 0 以上的整數';
}
if (empty($errors)) {
$quantity = (int) $quantity;
// 借出中的本數不會變,新總量必須至少能涵蓋這些借出中的書籍
$borrowed = $book['total'] - $book['available'];
if ($quantity < $borrowed) {
$errors[] = "數量不能小於目前已借出的 {$borrowed} 本";
} else {
$available = $quantity - $borrowed;
try {
$sql = "UPDATE books
SET title = :title, author = :author, isbn = :isbn,
category = :category, total = :total, available = :available
WHERE id = :id";
$stmt = $pdo->prepare($sql);
$stmt->execute([
':title' => $title,
':author' => $author,
':isbn' => $isbn,
':category' => $category !== '' ? $category : null,
':total' => $quantity,
':available' => $available,
':id' => $id,
]);
// 更新成功導回列表頁
header('Location: index.php');
exit;
} catch (PDOException $e) {
// 23000 為唯一鍵衝突(ISBN 重複),其餘視為一般更新錯誤
if ($e->getCode() === '23000') {
$errors[] = '這個 ISBN 已經存在,請確認後再試一次';
} else {
$errors[] = '更新資料庫失敗,請稍後再試';
}
}
}
}
}
?>
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<title>編輯書籍 - 圖書管理系統</title>
<style>
body {
font-family: "Microsoft JhengHei", Arial, sans-serif;
background-color: #f5f5f5;
color: #333;
margin: 0;
padding: 2rem;
}
h1 {
text-align: center;
margin-bottom: 1.5rem;
}
form {
width: 100%;
max-width: 480px;
margin: 0 auto;
background-color: #fff;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
padding: 1.5rem 2rem;
box-sizing: border-box;
}
.field {
margin-bottom: 1rem;
}
label {
display: block;
margin-bottom: 0.35rem;
font-weight: bold;
}
input[type="text"],
input[type="number"] {
width: 100%;
padding: 0.5rem;
border: 1px solid #ccc;
border-radius: 4px;
box-sizing: border-box;
font-size: 1rem;
}
button {
width: 100%;
padding: 0.75rem;
background-color: #2c3e50;
color: #fff;
border: none;
border-radius: 4px;
font-size: 1rem;
cursor: pointer;
}
button:hover {
background-color: #1a252f;
}
.errors {
max-width: 480px;
margin: 0 auto 1rem;
background-color: #fdecea;
color: #b71c1c;
border: 1px solid #f5c6cb;
border-radius: 4px;
padding: 0.75rem 1rem;
}
.errors ul {
margin: 0;
padding-left: 1.25rem;
}
.back-link {
display: block;
max-width: 480px;
margin: 1rem auto 0;
text-align: center;
}
</style>
</head>
<body>
<h1>編輯書籍</h1>
<?php // 有驗證或更新錯誤時,在表單上方列出所有錯誤訊息 ?>
<?php if (!empty($errors)): ?>
<div class="errors">
<ul>
<?php foreach ($errors as $error): ?>
<li><?= htmlspecialchars($error) ?></li>
<?php endforeach; ?>
</ul>
</div>
<?php endif; ?>
<form method="POST" action="edit_book.php?id=<?= (int) $id ?>">
<div class="field">
<label for="title">書名</label>
<input type="text" id="title" name="title" value="<?= htmlspecialchars($title) ?>" required>
</div>
<div class="field">
<label for="author">作者</label>
<input type="text" id="author" name="author" value="<?= htmlspecialchars($author) ?>" required>
</div>
<div class="field">
<label for="isbn">ISBN</label>
<input type="text" id="isbn" name="isbn" value="<?= htmlspecialchars($isbn) ?>" required>
</div>
<div class="field">
<label for="category">類別</label>
<input type="text" id="category" name="category" value="<?= htmlspecialchars($category ?? '') ?>">
</div>
<div class="field">
<label for="quantity">數量</label>
<input type="number" id="quantity" name="quantity" value="<?= htmlspecialchars($quantity) ?>" min="0" required>
</div>
<button type="submit">儲存變更</button>
</form>
<a class="back-link" href="index.php">返回書籍列表</a>
</body>
</html>
資安非常強調一點:絕對不能使用 GET 請求執行刪除操作(例如 delete_book.php?id=1),否則可能字串拼接或是被搜尋引擎爬蟲點擊造成資料意外刪除。因此,使用 POST 請求來處理刪除操作。
對 Claude Code 下的 Prompt:
請幫我在 ~/library-system/delete_book.php 建立刪除書籍功能:
1. 僅允許 POST 請求方式。若為 GET 請求則直接重導向至 index.php。
2. 接收 POST 參數 id,驗證是否為有效整數。
3. 使用 Prepared Statement 執行 DELETE FROM books WHERE id = :id。
4. 刪除完成後,重新導向至 index.php。
產生的 delete_book.php
<?php
require __DIR__ . '/config/database.php';
// 只接受 POST 請求,避免用網址直接觸發刪除
if ($_SERVER['REQUEST_METHOD'] !== 'POST') {
header('Location: index.php');
exit;
}
// 驗證要刪除的 id 格式,不合法就導回列表頁
$id = $_POST['id'] ?? null;
if ($id === null || !ctype_digit((string) $id) || (int) $id < 1) {
header('Location: index.php');
exit;
}
$id = (int) $id;
$pdo = Database::getConnection();
try {
$stmt = $pdo->prepare("DELETE FROM books WHERE id = :id");
$stmt->execute([':id' => $id]);
} catch (PDOException $e) {
// 刪除失敗仍導回列表頁,不中斷使用者流程
}
// 不論成功或失敗都導回列表頁
header('Location: index.php');
exit;
index.php 表格(新增操作欄位)有了編輯與刪除頁面後,還需要更新首頁的表格,讓使用者可以直接在列表中點擊操作。
對 Claude Code 下 Prompt:
請幫我修改 ~/library-system/index.php 頁面的表格,新增一個「操作」欄位以串接編輯與刪除功能:
1. 表格標頭:在 table 的 thead 最右側新增 th 標題「操作」。
2. 資料欄位:在 tbody 的 foreach 迴圈最右側新增 td 欄位,內含:
- 「編輯」連結:指向 edit_book.php?id=【書籍ID】。
- 「刪除」表單:使用 POST 方式傳送至 delete_book.php,內含隱藏欄位 input (name="id") 帶入書籍 ID。
- 刪除二次確認:在刪除表單加上 onsubmit,跳出 JavaScript 確認框,顯示「確定要刪除《【書名】》嗎?」(書名需做 htmlspecialchars 轉義)。
3. 樣式與排版優化:
- 讓「編輯」連結與「刪除」表單按鈕在同一個 td 內保持水平並排 (inline / flex)。
- 刪除按鈕樣式改為紅色文字連結風格,並加上 cursor: pointer。
- 保持現有的表格整體樣式風格統一。
更新後的 index.php:
<?php
require __DIR__ . '/config/database.php';
$pdo = Database::getConnection();
// 查出所有書籍,依 id 由新到舊排序,供下方表格顯示
$sql = "SELECT id, title, author, category, available, created_at FROM books ORDER BY id DESC";
$stmt = $pdo->query($sql);
$books = $stmt->fetchAll();
?>
<!DOCTYPE html>
<html lang="zh-Hant">
<head>
<meta charset="UTF-8">
<title>書籍列表 - 圖書管理系統</title>
<style>
body {
font-family: "Microsoft JhengHei", Arial, sans-serif;
background-color: #f5f5f5;
color: #333;
margin: 0;
padding: 2rem;
}
h1 {
text-align: center;
margin-bottom: 1.5rem;
}
table {
width: 100%;
max-width: 960px;
margin: 0 auto;
border-collapse: collapse;
background-color: #fff;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
}
th, td {
padding: 0.75rem 1rem;
text-align: left;
border-bottom: 1px solid #ddd;
}
th {
background-color: #2c3e50;
color: #fff;
}
tr:hover {
background-color: #f1f1f1;
}
.empty {
text-align: center;
padding: 2rem;
color: #777;
}
.actions {
display: flex;
align-items: center;
gap: 0.75rem;
}
.edit-link {
color: #2c3e50;
text-decoration: none;
}
.edit-link:hover {
text-decoration: underline;
}
.delete-form {
display: inline;
margin: 0;
}
.delete-btn {
background: none;
border: none;
padding: 0;
font: inherit;
color: #c0392b;
cursor: pointer;
text-decoration: underline;
}
.delete-btn:hover {
color: #922b21;
}
</style>
</head>
<body>
<h1>書籍列表</h1>
<table>
<thead>
<tr>
<th>ID</th>
<th>書名</th>
<th>作者</th>
<th>類別</th>
<th>可借數量</th>
<th>建立時間</th>
<th>操作</th>
</tr>
</thead>
<tbody>
<?php if (empty($books)): ?>
<!-- 沒有書籍資料時顯示提示列 -->
<tr>
<td class="empty" colspan="7">目前尚無書籍資料</td>
</tr>
<?php else: ?>
<?php foreach ($books as $book): ?>
<?php // 先跳脫書名,供刪除按鈕的 confirm 對話框安全嵌入 JS 字串使用 ?>
<?php $confirmTitle = htmlspecialchars(addslashes($book['title']), ENT_QUOTES, 'UTF-8'); ?>
<tr>
<td><?= htmlspecialchars($book['id']) ?></td>
<td><?= htmlspecialchars($book['title']) ?></td>
<td><?= htmlspecialchars($book['author']) ?></td>
<td><?= htmlspecialchars($book['category']) ?></td>
<td><?= htmlspecialchars($book['available']) ?></td>
<td><?= htmlspecialchars($book['created_at']) ?></td>
<td>
<div class="actions">
<a class="edit-link" href="edit_book.php?id=<?= htmlspecialchars($book['id']) ?>">編輯</a>
<form class="delete-form" method="POST" action="delete_book.php" onsubmit="return confirm('確定要刪除《<?= $confirmTitle ?>》嗎?')">
<input type="hidden" name="id" value="<?= htmlspecialchars($book['id']) ?>">
<button type="submit" class="delete-btn">刪除</button>
</form>
</div>
</td>
</tr>
<?php endforeach; ?>
<?php endif; ?>
</tbody>
</table>
</body>
</html>
更新後的結果:
輸入網址 http://localhost:8000/index.php,進行以下四種情境驗證:
index.php。今天完成了書籍資料的編輯、刪除與前端介面串接,已經完整建立了 books 資料表的 CRUD 基礎功能。
不過目前的程式碼並未檢查「操作者的權限」。現階段專注在建置 CRUD 的核心邏輯;但在真實環境中,若缺乏身份驗證與角色權限控管,任何人都能透過 API 工具直接對 delete_book.php 發送請求來清空資料。因此在正式上線前,後端必須導入管理者 Session 驗證機制,才能有效防範越權存取的資安風險。